package com.zxx.casserver.modules.user.service.impl;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.common.base.Joiner;
import com.zxx.cascommon.base.exception.*;
import com.zxx.cascommon.base.utils.HashUtil;
import com.zxx.cascommon.base.utils.JwtTokenUtils;
import com.zxx.casserver.base.enums.UserRelationTypeEnum;
import com.zxx.casserver.base.redis.RedisKeyConstants;
import com.zxx.casserver.base.redis.RedisService;
import com.zxx.casserver.modules.app.service.IAppService;
import com.zxx.casserver.modules.org.entity.OrgDO;
import com.zxx.casserver.modules.org.service.IOrgService;
import com.zxx.casserver.modules.role.service.IRoleService;
import com.zxx.casserver.modules.user.entity.*;
import com.zxx.casserver.modules.user.mapper.UserMapper;
import com.zxx.casserver.modules.user.service.IUserAccountService;
import com.zxx.casserver.modules.user.service.IUserRelationService;
import com.zxx.casserver.modules.user.service.IUserService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.ObjectUtils;

import java.util.*;

/**
 * 用户管理-逻辑实现层
 *
 * @author zhoukai
 * @date 2020/6/24 17:16
 */
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, UserDO> implements IUserService {

    private final RedisService redisService;

    private final IUserRelationService userRelationService;

    private final IUserAccountService userAccountService;

    private final IOrgService orgService;

    private final IRoleService roleService;

    private final IAppService appService;

    public UserServiceImpl(RedisService redisService, IUserRelationService userRelationService,
                           IUserAccountService userAccountService, IOrgService orgService,
                           IRoleService roleService, IAppService appService) {
        this.redisService = redisService;
        this.userRelationService = userRelationService;
        this.userAccountService = userAccountService;
        this.orgService = orgService;
        this.roleService = roleService;
        this.appService = appService;
    }

    @Override
    public IPage<UserDO> listUser(UserVO userVO) {
        Page<UserDO> page = new Page<>(userVO.getCurPage(), userVO.getPageSize());
        QueryWrapper<UserDO> queryWrapper = new QueryWrapper<>();
        //如果要根据账户查询用户信息，则先查询账户表，返回所有的用户UID
        if (!ObjectUtils.isEmpty(userVO.getAccount())) {
            Set<Long> uidSet = userAccountService.getUidByAccount(userVO.getAccount());
            if (!ObjectUtils.isEmpty(uidSet)) {
                queryWrapper.in("uid", uidSet);
            }
        }

        if (!ObjectUtils.isEmpty(userVO.getPlatformName())) {
            Set<Long> platformIdSet = orgService.getOrgIdByName(userVO.getPlatformName());
            if (!ObjectUtils.isEmpty(platformIdSet)) {
                queryWrapper.in("platform_id", platformIdSet);
            }
        }

        if (!ObjectUtils.isEmpty(userVO.getOrgName())) {
            Set<Long> orgIdSet = orgService.getOrgIdByName(userVO.getPlatformName());
            if (!ObjectUtils.isEmpty(orgIdSet)) {
                queryWrapper.in("org_id", orgIdSet);
            }
        }

        if (!ObjectUtils.isEmpty(userVO.getIds())) {
            queryWrapper.in("uid", userVO.getIds());
        }
        if (!ObjectUtils.isEmpty(userVO.getUid())) {
            queryWrapper.eq("uid", userVO.getUid());
        }
        if (!ObjectUtils.isEmpty(userVO.getName())) {
            queryWrapper.eq("name", userVO.getName());
        }
        if (!ObjectUtils.isEmpty(userVO.getEmail())) {
            queryWrapper.eq("email", userVO.getEmail());
        }
        if (!ObjectUtils.isEmpty(userVO.getPhone())) {
            queryWrapper.eq("phone", userVO.getPhone());
        }
        if (!ObjectUtils.isEmpty(userVO.getStatus())) {
            queryWrapper.eq("status", userVO.getStatus());
        }
        if (!ObjectUtils.isEmpty(userVO.getIcon())) {
            queryWrapper.eq("icon", userVO.getIcon());
        }
        if (!ObjectUtils.isEmpty(userVO.getStartTime())) {
            queryWrapper.ge("create_time", userVO.getStartTime());
        }
        if (!ObjectUtils.isEmpty(userVO.getEndTime())) {
            queryWrapper.le("create_time", userVO.getEndTime());
        }
        if (!ObjectUtils.isEmpty(userVO.getColumns()) && !ObjectUtils.isEmpty(userVO.getAsc())) {
            String columnNames = Joiner.on(",").join(userVO.getColumns());
            if (userVO.getAsc()) {
                queryWrapper.orderByAsc(columnNames);
            } else {
                queryWrapper.orderByDesc(columnNames);
            }
        } else {
            queryWrapper.orderByDesc("create_time");
        }
        return page(page, queryWrapper);
    }


    @Override
    @Transactional(rollbackFor = Exception.class)
    public UserDO saveUser(UserVO userVO) {
        //判断账户是否唯一
        QueryWrapper<UserAccountDO> accountQueryWrapper = new QueryWrapper<>();
        accountQueryWrapper.eq("account", userVO.getAccount());
        UserAccountDO accountUser = userAccountService.getOne(accountQueryWrapper);
        if (!ObjectUtils.isEmpty(accountUser)) {
            throw new DataAlreadyExistException("账户已经存在，请重新输入");
        }

        //保存用户账户信息
        UserAccountDO userAccountDO = new UserAccountDO();
        userAccountDO.setAccount(userVO.getAccount());
        userAccountDO.setPassword(HashUtil.encodeUserPassword(userVO.getPassword()));
        userAccountService.saveUserAccount(userAccountDO);
        if (ObjectUtils.isEmpty(userAccountDO.getUid())) {
            throw new ParamsException("账户ID生成异常，请重新注册");
        }

        //保存用户基础信息
        UserDO userDO = JSON.parseObject(JSON.toJSONString(userVO), UserDO.class);
        userDO.setUid(userAccountDO.getUid());
        userDO.setCreateTime(System.currentTimeMillis());
        userDO.setUpdateTime(System.currentTimeMillis());
        return save(userDO) ? userDO : null;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public UserDO updateUser(UserVO userVO) {
        //如果修改账户，则判断账户是否唯一
        if (!ObjectUtils.isEmpty(userVO.getAccount())) {
            QueryWrapper<UserAccountDO> accountQueryWrapper = new QueryWrapper<>();
            accountQueryWrapper.eq("account", userVO.getAccount());
            accountQueryWrapper.notIn("uid", userVO.getUid());
            UserAccountDO accountUser = userAccountService.getOne(accountQueryWrapper);
            if (!ObjectUtils.isEmpty(accountUser)) {
                throw new DataAlreadyExistException("账户已经存在，请重新输入");
            }
            //修改账户
            QueryWrapper<UserAccountDO> uidQueryWrapper = new QueryWrapper<>();
            uidQueryWrapper.eq("uid", userVO.getUid());
            UserAccountDO oldAccountUser = userAccountService.getOne(uidQueryWrapper);
            if (ObjectUtils.isEmpty(oldAccountUser)) {
                throw new DataNotExistException("账户不存在，无法修改");
            }
        }

        QueryWrapper<UserDO> userQueryWrapper = new QueryWrapper<>();
        userQueryWrapper.eq("uid", userVO.getUid());
        userQueryWrapper.eq("app_id", userVO.getAppId());
        UserDO oldUser = getOne(userQueryWrapper);
        if (ObjectUtils.isEmpty(oldUser)) {
            throw new DataNotExistException();
        }

        UserDO userDO = checkChange(userVO, oldUser);
        if (ObjectUtils.isEmpty(userDO)) {
            throw new DataNoChangeException();
        }

        return updateById(userDO) ? userDO : null;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public UserDO removeUser(UserVO userVO) {
        UserDO oldUser = getById(userVO.getUid());
        if (ObjectUtils.isEmpty(oldUser)) {
            throw new DataNotExistException();
        }
        boolean isDelete = removeById(userVO.getUid());
        if (!isDelete) {
            return null;
        }
        //删除用户关联的角色或者其他关联信息
        HashMap<String, Object> delMap = new HashMap<>(4);
        delMap.put("uid", oldUser.getUid());

        return userRelationService.removeByMap(delMap) ? oldUser : null;
    }

    @Override
    public UserVO login(UserVO userVO) {
        //判断验证码是否正常
        String verifyCode = redisService.get(RedisKeyConstants.getVerifyCode(userVO.getRequestId()));
        if(ObjectUtils.isEmpty(verifyCode)){
            throw new ParamsException("验证码过期，请重新申请验证码");
        }
        if(!verifyCode.equals(userVO.getVerifyCode())){
            throw new ParamsException("验证码错误，请重新输入");
        }

        //判断是否有异常登陆情况
        String loginError = redisService.get(RedisKeyConstants.getLoginError(userVO.getAccount()));
        if(!ObjectUtils.isEmpty(loginError)){
            int times = Integer.parseInt(loginError);
            if(times  > 2){
                throw new TokenErrorException("用户异常登陆超过3次，锁定30分钟，请稍后再试");
            }
        }

        QueryWrapper<UserAccountDO> accountQueryWrapper = new QueryWrapper<>();
        accountQueryWrapper.eq("account", userVO.getAccount());
        UserAccountDO oldUserAccount = userAccountService.getOne(accountQueryWrapper);
        if (ObjectUtils.isEmpty(oldUserAccount)) {
            throw new DataNotExistException();
        }

        String password = HashUtil.encodeUserPassword(userVO.getPassword());
        if (!password.equals(oldUserAccount.getPassword())) {
            //种上登陆异常的KEY，如果次数超过3次则锁定半个小时
            redisService.increment(RedisKeyConstants.getLoginError(userVO.getAccount()),1);
            throw new PasswordErrorException();
        }

        //判断当前用户是否有权限进入该系统，并且判断该用户是否有当前系统的账户，如果没有账户则创建账户
        QueryWrapper<UserRelationDO> queryUserRelationWrapper = new QueryWrapper<>();
        queryUserRelationWrapper.eq("uid", userVO.getUid());
        queryUserRelationWrapper.eq("type", UserRelationTypeEnum.SYSTEM.getIndex());
        queryUserRelationWrapper.eq("app_id", userVO.getAppId());
        UserRelationDO userRelationDO = userRelationService.getOne(queryUserRelationWrapper);
        if (ObjectUtils.isEmpty(userRelationDO)) {
            throw new TokenErrorException("当前用户无权限登陆该系统");
        }

        //根据登陆成功的UID查询用户基础信息
        UserVO userBO = getUser(userVO.getUid(), userVO.getAppId());
        if (ObjectUtils.isEmpty(userBO)) {
            throw new DataNotExistException("登陆失败，没有查询到当前用户信息");
        }

        //创建jwtToken以及用户信息
        userBO.setToken(JwtTokenUtils.createToken(userBO.getUid()));
        //缓存，用来保存用户登陆状态
        redisService.set(RedisKeyConstants.getTokenName(userBO.getToken()), JSON.toJSONString(userBO));
        return userBO;
    }

    /**
     * 查询用户信息
     *
     * @param uid   用户主键
     * @param appId 系统ID
     * @return 返回查询对象
     */
    private UserVO getUser(Long uid, Long appId) {
        QueryWrapper<UserDO> userQueryWrapper = new QueryWrapper<>();
        userQueryWrapper.eq("uid", uid);
        userQueryWrapper.eq("app_id", appId);
        UserDO oldUser = getOne(userQueryWrapper);
        if (ObjectUtils.isEmpty(oldUser)) {
            return null;
        }
        UserVO userVO = JSON.parseObject(JSON.toJSONString(oldUser), UserVO.class);

        //获取用户账户信息
        QueryWrapper<UserAccountDO> accountQueryWrapper = new QueryWrapper<>();
        accountQueryWrapper.eq("uid", uid);
        UserAccountDO oldUserAccount = userAccountService.getOne(accountQueryWrapper);
        if (!ObjectUtils.isEmpty(oldUserAccount)) {
            userVO.setAccount(oldUserAccount.getAccount());
        }

        //拼接企业信息
        Set<Long> orgIdSet = new HashSet<>();
        orgIdSet.add(userVO.getOrgId());
        orgIdSet.add(userVO.getPlatformId());

        if (ObjectUtils.isEmpty(orgIdSet)) {
            return userVO;
        }

        List<OrgDO> orgDOList = orgService.listByIds(orgIdSet);
        if (ObjectUtils.isEmpty(orgDOList)) {
            return userVO;
        }
        Map<Long, String> orgMap = new HashMap<>();
        for (OrgDO orgDO : orgDOList) {
            orgMap.put(orgDO.getOrgId(), orgDO.getName());
        }

        userVO.setOrgName(orgMap.get(userVO.getOrgId()));
        userVO.setPlatformName(orgMap.get(userVO.getPlatformId()));

        //获取用户关联的信息
        QueryWrapper<UserRelationDO> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("uid", uid);
        List<UserRelationDO> userRelationList = userRelationService.list(queryWrapper);
        Set<Long> roleIdSet = new HashSet<>();
        Set<Long> appIdSet = new HashSet<>();
        if (!ObjectUtils.isEmpty(userRelationList)) {
            for (UserRelationDO userRelationDO : userRelationList) {
                if (UserRelationTypeEnum.ROLE.getIndex().equals(userRelationDO.getType())) {
                    roleIdSet.add(userRelationDO.getRelationId());
                } else if (UserRelationTypeEnum.SYSTEM.getIndex().equals(userRelationDO.getType())) {
                    appIdSet.add(userRelationDO.getRelationId());
                }
            }
        }
        //查询用户角色
        if (!ObjectUtils.isEmpty(roleIdSet)) {
            userVO.setRoleList(roleService.listByIds(roleIdSet));
        }

        //查询系统信息
        if (!ObjectUtils.isEmpty(appIdSet)) {
            userVO.setAppList(appService.listByIds(appIdSet));
        }
        return userVO;
    }

    @Override
    public UserVO checkLogin(UserVO userVO) {
        //判断token是否已经存redis
        String token = redisService.get(RedisKeyConstants.getTokenName(userVO.getToken()));
        if (!ObjectUtils.isEmpty(token)) {
            return JSON.parseObject(token, UserVO.class);
        }
        return null;
    }

    @Override
    public Boolean loginOut(UserVO userVO) {
        //检查是否已经登陆如果非登陆状态，直接返回登出
        UserVO userBO = checkLogin(userVO);
        if (ObjectUtils.isEmpty(userBO)) {
            return true;
        }
        boolean loginOut = redisService.delete(RedisKeyConstants.getTokenName(userVO.getToken()));
        return loginOut;
    }

    @Override
    public IPage<UserVO> dispose(IPage<UserDO> page) {
        //获取所属平台和所属企业的ID
        Set<Long> orgIdSet = new HashSet<>();
        //拼接所属平台名称
        if (!ObjectUtils.isEmpty(page.getRecords())) {
            for (UserDO userDO : page.getRecords()) {
                orgIdSet.add(userDO.getPlatformId());
                orgIdSet.add(userDO.getOrgId());
            }
        }
        //用来保存组织与名称的值
        Map<Long, String> orgMap = new HashMap<>(64);
        //根据组织ID批量查询组织信息，拼接对应的组织名称回去
        if (!ObjectUtils.isEmpty(orgIdSet)) {
            List<OrgDO> orgList = orgService.listByIds(orgIdSet);
            if (!ObjectUtils.isEmpty(orgList)) {
                for (OrgDO basicOrg : orgList) {
                    orgMap.put(basicOrg.getOrgId(), basicOrg.getName());
                }
            }
        }
        List<UserVO> userVOList = new ArrayList<>(32);
        for (UserDO userDO : page.getRecords()) {
            UserVO userVO = JSON.parseObject(JSON.toJSONString(userDO), UserVO.class);
            userVO.setOrgName(orgMap.get(userDO.getOrgId()));
            userVO.setPlatformName(orgMap.get(userDO.getPlatformId()));
            userVOList.add(userVO);
        }
        IPage<UserVO> userPage = new Page<>();
        userPage.setRecords(userVOList);
        userPage.setSize(page.getSize());
        userPage.setPages(page.getPages());
        userPage.setTotal(page.getTotal());
        userPage.setCurrent(page.getCurrent());
        return userPage;
    }

    @Override
    public Boolean resetPassword(UserVO userVO) {
        QueryWrapper<UserAccountDO> accountQueryWrapper = new QueryWrapper<>();
        accountQueryWrapper.eq("uid", userVO.getUid());
        UserAccountDO accountUser = userAccountService.getOne(accountQueryWrapper);
        if (ObjectUtils.isEmpty(accountUser)) {
            throw new DataNotExistException("账户不存在，请重新输入");
        }
        //修改账户
        UserAccountDO userAccountDO = new UserAccountDO();
        userAccountDO.setUid(userVO.getUid());
        userAccountDO.setPassword(HashUtil.encodeUserPassword(userVO.getPassword()));

        return userAccountService.updateById(userAccountDO);
    }

    @Override
    public UserVO getUser(UserVO userVO) {
        return getUser(userVO.getUid(), userVO.getAppId());
    }

    @Override
    public Boolean saveRelation(UserRelationVO userRelationVO) {
        //删除之前旧的用户关联关系
        Boolean isDelete = userRelationService.removeUserRelation(userRelationVO);
        if (ObjectUtils.isEmpty(isDelete) || !isDelete) {
            return false;
        }

        List<UserRelationDO> userRelationList = new ArrayList<>();
        for (Long relationId : userRelationVO.getRelationList()) {
            UserRelationDO userRelationDO = new UserRelationDO();
            userRelationDO.setUid(userRelationVO.getUid());
            userRelationDO.setType(userRelationVO.getType());
            userRelationDO.setRelationId(relationId);
            userRelationDO.setCreateTime(System.currentTimeMillis());
            userRelationList.add(userRelationDO);
        }

        return userRelationService.saveBatch(userRelationList);
    }

    @Override
    public void saveVerifyCode(String requestId, String verifyCode) {
        redisService.set(RedisKeyConstants.getVerifyCode(requestId),verifyCode,600L);
    }

    /**
     * 比较传入参数与数据库查询参数是否一致,只更新改变的值
     *
     * @return 返回有变化值的对象
     * @author zhoukai
     * @date 2020/7/30 15:55
     */
    private UserDO checkChange(UserVO userVO, UserDO oldUser) {
        UserDO userDO = new UserDO();
        userDO.setUid(userVO.getUid());
        userDO.setAppId(userVO.getAppId());
        userDO.setUpdateTime(System.currentTimeMillis());

        boolean isChange = false;

        if (!ObjectUtils.isEmpty(userVO.getPlatformId()) && !userVO.getPlatformId().equals(oldUser.getPlatformId())) {
            userDO.setPlatformId(userVO.getPlatformId());
            isChange = true;
        }

        if (!ObjectUtils.isEmpty(userVO.getOrgId()) && !userVO.getOrgId().equals(oldUser.getOrgId())) {
            userDO.setOrgId(userVO.getOrgId());
            isChange = true;
        }

        if (!ObjectUtils.isEmpty(userVO.getName()) && !userVO.getName().equals(oldUser.getName())) {
            userDO.setName(userVO.getName());
            isChange = true;
        }

        if (!ObjectUtils.isEmpty(userVO.getIcon()) && !userVO.getIcon().equals(oldUser.getIcon())) {
            userDO.setIcon(userVO.getIcon());
            isChange = true;
        }

        if (!ObjectUtils.isEmpty(userVO.getPhone()) && !userVO.getPassword().equals(oldUser.getPhone())) {
            userDO.setPhone(userVO.getPhone());
            isChange = true;
        }

        if (!ObjectUtils.isEmpty(userVO.getEmail()) && !userVO.getEmail().equals(oldUser.getEmail())) {
            userDO.setEmail(userVO.getEmail());
            isChange = true;
        }

        if (!ObjectUtils.isEmpty(userVO.getStatus()) && !userVO.getStatus().equals(oldUser.getStatus())) {
            userDO.setStatus(userVO.getStatus());
            isChange = true;
        }

        return isChange ? userDO : null;
    }
}
